// This file is a part of Funky C++.
// Copyright (C) 2011  Valentin David
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
#ifndef FILTER_HH
# define FILTER_HH

# include "signal.hh"

namespace funky {

  template <typename Signal, typename Function>
  struct signal_filter {
  private:
    typedef typename signal_traits<Signal>::duration duration;
    Signal signal;
    Function function;

  public:
    template <typename F, typename S>
    signal_filter(F&& function, S&& signal)
      : signal(std::forward<S>(signal)),
	function(std::forward<F>(function)) {}

    signal_filter() = delete;

    signal_filter(const signal_filter&) = default;

    signal_filter(signal_filter&& other)
      : signal(std::move(other.signal)),
	function(std::move(other.function)) {}

    signal_filter& operator=(const signal_filter& other) {
      return *this = signal_filter(other);
    }

    signal_filter& operator=(signal_filter&& other) {
      std::swap(signal, other.signal);
      std::swap(function, other.function);
      return *this;
    }

    decltype(function(*signal))
    operator*() const {
      return function(*signal);
    }

    signal_filter& operator+=(const duration& d) {
      signal += d;
      return *this;
    }

    signal_filter& operator++() {
      ++signal;
      return *this;
    }
  };


  template <typename Signal, typename Filter>
  struct signal_traits<signal_filter<Signal, Filter> > {
    typedef typename signal_traits<Signal>::duration duration;
    typedef typename signal_traits<Signal>::sample src_sample;
    typedef
    decltype(std::declval<Filter>()(std::declval<src_sample>()))
    sample;
  };

  template <typename F, typename S,
	    typename Function = typename std::decay<F>::type,
	    typename Signal = typename std::decay<S>::type,
	    typename = typename signal_traits<Signal>::sample>
  signal_filter<Signal, Function> filter(F&& f, S&& s) {
    return signal_filter<Signal, Function>{
      std::forward<F>(f),
	std::forward<S>(s) };
  }

}

#endif
